package mrpanyu.guitool.dbcodegen;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import mrpanyu.guitool.base.annotation.Action;
import mrpanyu.guitool.base.annotation.OnLoad;
import mrpanyu.guitool.base.annotation.OnParameterChange;
import mrpanyu.guitool.base.annotation.Parameter;
import mrpanyu.guitool.base.annotation.ToolModel;
import mrpanyu.guitool.base.model.ParameterType;
import mrpanyu.guitool.base.model.Tool;
import mrpanyu.guitool.dbcodegen.chain.DocHtmlProcessorChain;
import mrpanyu.guitool.dbcodegen.chain.JpaProcessorChain;
import mrpanyu.guitool.dbcodegen.chain.MyBatisPlusProcessorChain;
import mrpanyu.guitool.dbcodegen.model.Database;
import mrpanyu.guitool.dbcodegen.processor.DbModelProcessor;
import mrpanyu.guitool.dbcodegen.reader.DbModelReader;
import mrpanyu.guitool.dbcodegen.reader.MySqlJDBCModelReader;
import mrpanyu.guitool.dbcodegen.reader.OracleJDBCModelReader;
import mrpanyu.guitool.dbcodegen.util.DbCodeGeneratorUtils;

@ToolModel(displayName = "数据库代码生成工具", enableProfiles = true)
public class DbCodeGeneratorTool {

	@Parameter(order = 1, displayName = "模型读取方式", type = ParameterType.SELECT, options = {})
	private String reader;

	@Parameter(order = 2, displayName = "JDBC驱动类", description = "可空，一般能自动识别", visible = false)
	private String driverClassName;

	@Parameter(order = 3, displayName = "JDBC URL", visible = false)
	private String url;

	@Parameter(order = 4, displayName = "用户名", visible = false)
	private String user;

	@Parameter(order = 5, displayName = "密码", visible = false)
	private String password;

	@Parameter(order = 6, displayName = "Schema", visible = false)
	private String schema;

	@Parameter(order = 7, displayName = "表名LIKE", description = "多个逗号分隔，可以用%作为通配符LIKE匹配", visible = false)
	private String tableNames = "%";

	@Parameter(order = 50, displayName = "生成流程", type = ParameterType.SELECT, options = {})
	private String chain;

	@Parameter(order = 51, displayName = "输出根路径", type = ParameterType.DIRECTORY)
	private File baseDir;

	@Parameter(order = 52, displayName = "java包名", description = "java根包名称，例如com.example.project", visible = false)
	private String basePackage;

	@Parameter(order = 53, displayName = "编码", type = ParameterType.SELECT, options = { "UTF-8",
			"GB18030" }, visible = false)
	private String encoding = "UTF-8";

	private List<ComponentInfo> registeredReaders = new ArrayList<>();
	private List<ComponentInfo> registeredChains = new ArrayList<>();

	@OnLoad
	public void init(Tool tool) {
		// 注册可用的reader
		registerReader("MySql反向生成", MySqlJDBCModelReader.class, "driverClassName", "url", "user", "password", "schema",
				"tableNames");
		registerReader("Oracle反向生成", OracleJDBCModelReader.class, "driverClassName", "url", "user", "password",
				"schema", "tableNames");

		// 注册可用的chain
		registerChain("HTML文档生成", DocHtmlProcessorChain.class, "baseDir");
		registerChain("JPA实体类生成", JpaProcessorChain.class, "baseDir", "basePackage", "encoding");
		registerChain("MyBatisPlus代码生成", MyBatisPlusProcessorChain.class, "baseDir", "basePackage", "encoding");

		// 根据reader和chain生成选项
		buildOptions(tool);
	}

	/**
	 * 注册reader类
	 * 
	 * @param name        显示名称
	 * @param readerClass reader类
	 * @param params      这个reader需要的参数，需与DbCodeGeneratorTool标注@Parameter的参数同名，会通过setProperty方式注入进去
	 */
	private void registerReader(String name, Class<? extends DbModelReader> readerClass, String... params) {
		registeredReaders.add(buildComp(name, readerClass, params));
	}

	/**
	 * 注册chain类
	 * 
	 * @param name       显示名称
	 * @param chainClass chain类
	 * @param params     这个chain需要的参数，需与DbCodeGeneratorTool标注@Parameter的参数同名，会通过setProperty方式注入进去
	 */
	private void registerChain(String name, Class<? extends DbModelProcessor> chainClass, String... params) {
		registeredChains.add(buildComp(name, chainClass, params));
	}

	private ComponentInfo buildComp(String name, Class<?> cls, String... params) {
		ComponentInfo comp = new ComponentInfo();
		comp.name = name;
		comp.cls = cls;
		if (params != null) {
			comp.params.addAll(Arrays.asList(params));
		}
		return comp;
	}

	private void buildOptions(Tool tool) {
		List<String> optsReader = new ArrayList<>();
		optsReader.add("");
		registeredReaders.stream().map(c -> c.name).forEach(c -> optsReader.add(c));
		tool.getParameter("reader").setOptions(optsReader);
		List<String> optsChain = new ArrayList<>();
		optsChain.add("");
		registeredChains.stream().map(c -> c.name).forEach(c -> optsChain.add(c));
		tool.getParameter("chain").setOptions(optsChain);
	}

	@OnParameterChange("reader")
	public void onReaderChange(Tool tool) {
		// 隐藏reader相关所有参数
		for (String param : Arrays.asList("driverClassName", "url", "user", "password", "schema", "tableNames")) {
			tool.getParameter(param).setVisible(false);
		}

		if (DbCodeGeneratorUtils.isNotBlank(reader)) {
			// 找到相关reader
			ComponentInfo info = registeredReaders.stream().filter(c -> reader.equals(c.name)).findAny().get();
			// 显示reader需要的参数
			for (String param : info.params) {
				tool.getParameter(param).setVisible(true);
			}
		}
	}

	@OnParameterChange("chain")
	public void onChainChange(Tool tool) {
		// 隐藏chain相关所有参数
		for (String param : Arrays.asList("basePackage", "encoding")) {
			tool.getParameter(param).setVisible(false);
		}

		if (DbCodeGeneratorUtils.isNotBlank(chain)) {
			// 找到相关chain
			ComponentInfo info = registeredChains.stream().filter(c -> chain.equals(c.name)).findAny().get();
			// 显示chain需要的参数
			for (String param : info.params) {
				tool.getParameter(param).setVisible(true);
			}
		}
	}

	@Action(displayName = "代码生成", order = 1)
	public void generate(Tool tool) {
		tool.clearMessages();
		if (!validate(tool)) {
			return;
		}
		try {
			// 实例化reader
			ComponentInfo readerInfo = registeredReaders.stream().filter(c -> reader.equals(c.name)).findAny().get();
			DbModelReader reader = (DbModelReader) readerInfo.cls.getConstructor().newInstance();
			for (String param : readerInfo.params) {
				DbCodeGeneratorUtils.setProperty(reader, param, tool.getParameterValue(param));
			}
			// 实例化chain
			ComponentInfo chainInfo = registeredChains.stream().filter(c -> chain.equals(c.name)).findAny().get();
			DbModelProcessor chain = (DbModelProcessor) chainInfo.cls.getConstructor().newInstance();
			for (String param : chainInfo.params) {
				DbCodeGeneratorUtils.setProperty(chain, param, tool.getParameterValue(param));
			}
			// 执行
			tool.infoMessage("开始读取数据库元模型...");
			Database model = reader.read();
			tool.infoMessage("开始代码生成...");
			chain.process(model);

			tool.infoMessage("结束代码生成");
		} catch (Exception e) {
			tool.errorMessage(e);
		}
	}

	private boolean validate(Tool tool) {
		if (DbCodeGeneratorUtils.isBlank(reader)) {
			tool.errorMessage("模型读取方式不能为空");
			return false;
		}
		if (DbCodeGeneratorUtils.isBlank(chain)) {
			tool.errorMessage("生成流程不能为空");
			return false;
		}
		return true;
	}

	private class ComponentInfo {
		String name;
		Class<?> cls;
		List<String> params = new ArrayList<>();
	}

}
